/*******************************************************************************
  * 文件：Fota.c
  * 作者：zyz
  * 版本：v1.0.0
  * 日期：2019-06-21
  * 说明：固件升级
*******************************************************************************/
/* 头文件 *********************************************************************/
#include "Fota.h"
#include "Flash.h"
#include "ImageHeader.h"
#include "CRC.h"
#include "JumpFunction.h"
#include "Hardware.h"

/* 宏定义 *********************************************************************/
/* 类型定义 *******************************************************************/
/* 变量定义 *******************************************************************/
static FotaControl_ts sFotaControl;    // 升级控制

/* 函数声明 *******************************************************************/
/* 函数定义 *******************************************************************/
/*******************************************************************************
  * 函数名：Fota_Init
  * 功  能：初始化
  * 参  数：无
  * 返回值：无
  * 说  明：无
*******************************************************************************/
void Fota_Init(void)
{
    // 初始化flash
    Flash_Init();
    // 初始化状态
    sFotaControl.eState = eFOTA_STATE_READY;
}

/*******************************************************************************
  * 函数名：Fota_SetState
  * 功  能：设置状态
  * 参  数：FotaState_te eStatus - 状态
  * 返回值：无
  * 说  明：无
*******************************************************************************/
void Fota_SetState(FotaState_te eState)
{
    // 设置状态
    sFotaControl.eState = eState;
}

/*******************************************************************************
  * 函数名：Fota_GetState
  * 功  能：获取状态
  * 参  数：无
  * 返回值：状态
  * 说  明：无
*******************************************************************************/
FotaState_te Fota_GetState(void)
{
    // 返回状态
    return sFotaControl.eState;
}

/*******************************************************************************
  * 函数名：Fota_VerifyImageInfo
  * 功  能：校验程序信息
  * 参  数：ImageHeader_ts *psHeader - 头指针
  * 返回值：结果
  * 说  明：无
*******************************************************************************/
FotaVerifyResult_te Fota_VerifyImageInfo(ImageHeader_ts *psHeader)
{
    U16 u16Crc;
    FotaVerifyResult_te eResult = eFOTA_VERIFY_RESULT_ACCEPT;
    ImageHeader_ts *psBootloaderHeader  = (ImageHeader_ts *)u32BOOTLOADER_HEADER_ADDRESS;
    ImageHeader_ts *psApplicationHeader = (ImageHeader_ts *)u32APPLICATION_HEADER_ADDRESS;

    // 校验CRC
    u16Crc = CRC_CalcValue((U8 *)psHeader,
                           sizeof(ImageHeader_ts) - sizeof(U16),
                           u16IMAGE_CRC_SEED);
    // 校验失败
    if(u16Crc != psHeader->u16ImageHeaderCRC)
    {
        eResult = eFOTA_VERIFY_RESULT_REFUSE;
    }

    // 校验软件标识符
    if(eResult == eFOTA_VERIFY_RESULT_ACCEPT)
    {
        // 校验失败
        if(strcmp((const char *)psHeader->au8ImageId,
                  (const char *)psBootloaderHeader->au8ImageId) != 0)
        {
            eResult = eFOTA_VERIFY_RESULT_REFUSE;
        }
    }

    // 校验版本
    if(eResult == eFOTA_VERIFY_RESULT_ACCEPT)
    {
        // 校验当前程序
        if(VerifyImage(eIMAGE_TYPE_APPLICATION) == TRUE)
        {
            // 版本一致，不需要升级
            if((psHeader->u8MajorVersion == psApplicationHeader->u8MajorVersion) &&
               (psHeader->u8MinorVersion == psApplicationHeader->u8MinorVersion) &&
               (psHeader->u8RevisionVersion == psApplicationHeader->u8RevisionVersion) &&
               (psHeader->u16ReleaseYear == psApplicationHeader->u16ReleaseYear) &&
               (psHeader->u8ReleaseMonth == psApplicationHeader->u8ReleaseMonth) &&
               (psHeader->u8ReleaseDay == psApplicationHeader->u8ReleaseDay))
            {
                eResult = eFOTA_VERIFY_RESULT_NONEED;
            }
        }
    }

    // 校验程序地址
    if(eResult == eFOTA_VERIFY_RESULT_ACCEPT)
    {
        // 校验地址块对齐
        if((psHeader->u32ImageAddress % u16FOTA_FLASH_BLOCK_SIZE) != 0)
        {
            eResult = eFOTA_VERIFY_RESULT_REFUSE;
        }
        // 校验地址范围
        if(psHeader->u32ImageAddress <
           (psBootloaderHeader->u32ImageAddress + psBootloaderHeader->u32ImageSize))
        {
            eResult = eFOTA_VERIFY_RESULT_REFUSE;
        }
    }

    // 校验程序大小
    if(eResult == eFOTA_VERIFY_RESULT_ACCEPT)
    {
        // 校验大小块对齐
        if((psHeader->u32ImageSize % u16FOTA_FLASH_BLOCK_SIZE) != 0)
        {
            eResult = eFOTA_VERIFY_RESULT_REFUSE;
        }
        // 校验大小范围
        if((psHeader->u32ImageAddress + psHeader->u32ImageSize) >
           (u32FLASH_BASE_ADDRESS + u32FLASH_SIZE))
        {
            eResult = eFOTA_VERIFY_RESULT_REFUSE;
        }
    }

    // 返回结果
    return eResult;
}

/*******************************************************************************
  * 函数名：Fota_PrepareUpdate
  * 功  能：升级准备
  * 参  数：U32 u32Address - 地址
  *         U32 u32Size    - 大小
  * 返回值：无
  * 说  明：无
*******************************************************************************/
void Fota_PrepareUpdate(U32 u32Address, U32 u32Size)
{
    U16 u16Index;
    U16 u16StartBlock;
    U16 u16EndBlock;

    // 设置程序地址和大小
    sFotaControl.u32ImageAddress = u32Address;
    sFotaControl.u32ImageSize = u32Size;
    sFotaControl.u32WriteOffset = 0;

    // 计算起始和结束块
    u16StartBlock = (U16)(u32Address / u16FOTA_FLASH_BLOCK_SIZE);
    u16EndBlock   = (U16)((u32Address + u32Size - 1) / u16FOTA_FLASH_BLOCK_SIZE);

    // 擦除应用程序
    for(u16Index = u16StartBlock; u16Index <= u16EndBlock; u16Index++)
    {
        // 重启看门狗
        Hardware_RestartWdt();

        // 擦除块
        Hardware_EnterCritical();
        Flash_EraseBlock(u16Index);
        Hardware_ExitCritical();
    }
}

/*******************************************************************************
  * 函数名：Fota_GetImageDataPara
  * 功  能：获取程序数据参数
  * 参  数：U32 *pu32Offset - 偏移
  *         U8 *pu8Len      - 长度
  * 返回值：是否获取结束
  * 说  明：无
*******************************************************************************/
Bool Fota_GetImageDataPara(U32 *pu32Offset, U8 *pu8Len)
{
    Bool bReturn = FALSE;
    U32 u32LeftLen;

    // 写入程序状态
    if(sFotaControl.eState == eFOTA_STATE_WRITE_IMAGE)
    {
        // 未获取结束
        if(sFotaControl.u32WriteOffset < sFotaControl.u32ImageSize)
        {
            // 设置偏移
            *pu32Offset = sFotaControl.u32WriteOffset;
            // 计算剩余长度
            u32LeftLen = sFotaControl.u32ImageSize - sFotaControl.u32WriteOffset;
            // 剩余长度不小于默认写入长度
            if(u32LeftLen >= u8FOTA_DEFAULT_WRITE_BYTE_LEN)
            {
                *pu8Len = u8FOTA_DEFAULT_WRITE_BYTE_LEN;
            }
            // 剩余长度小于默认写入长度
            else
            {
                *pu8Len = (U8)u32LeftLen;
            }
        }
        // 获取结束
        else
        {
            // 返回获取结束
            // 清零偏移和长度
            bReturn = TRUE;
            *pu32Offset = 0;
            *pu8Len     = 0;
        }
    }
    // 不在写入状态
    else
    {
        // 清零偏移和长度
        *pu32Offset = 0;
        *pu8Len     = 0;
    }

    // 返回结果
    return bReturn;
}

/*******************************************************************************
  * 函数名：Fota_UpdateImageData
  * 功  能：更新程序数据
  * 参  数：U32 u32Offset - 偏移
  *         U8 *pu8Data   - 数据
  *         U8 u8Len      - 长度
  * 返回值：结果
  * 说  明：无
*******************************************************************************/
Bool Fota_UpdateImageData(U32 u32Offset, U8 *pu8Data, U8 u8Len)
{
    U32 u32WriteAddr;
    U8 u8WordCount;
    Bool bResult = FALSE;

    // 写入状态并且未写完成
    if((sFotaControl.eState == eFOTA_STATE_WRITE_IMAGE) &&
       (sFotaControl.u32WriteOffset <= sFotaControl.u32ImageSize))
    {
        if((u32Offset == sFotaControl.u32WriteOffset) &&
           (u8Len % 4 == 0))
        {
            // 计算写入地址和字数
            u32WriteAddr = sFotaControl.u32ImageAddress + u32Offset;
            u8WordCount  = u8Len / 4;

            // 写入flash
            Hardware_EnterCritical();
            bResult = Flash_WriteWords(u32WriteAddr, pu8Data, u8WordCount);
            Hardware_ExitCritical();
            // 更新写入偏移
            if(bResult)
            {
                sFotaControl.u32WriteOffset += u8Len;
            }
        }
    }

    // 返回结果
    return bResult;
}


/***************************** 文件结束 ***************************************/
